﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TerrainSystem;
using Utility;
using Utility.Math;

public class GameCamera : MonoBehaviour
{
	TerrainObject Terrain => Main.Terrain;

	public List<float> depressionAngle_List = new List<float> ();
	public float default_DepressionAngle = 65;
	public float throttleDepressionAngleToChangeLayer = 70;
	public float horizontalAngle = 0;
	public Vector3 focusPoint = Vector3.zero;
	public float height = 10;
	public Vector2 heightRange = new Vector2 (2, 10);

	public float rotateAngle = 45;
	public float moveSpeed = 3;

	public Vector2 range_min;
	public Vector2 range_max;

	new Camera camera;
	//forward on xz plane
	public Vector2 ForwardDir2D => transform.forward.XZ ();

	private int current_DepressionAngle_Index = 0;
	public float DepressionAngle => depressionAngle_List.TryGet (current_DepressionAngle_Index, out var angle) ? angle : default_DepressionAngle;

	float startHeight;
	private void Start ()
	{
		camera = GetComponent<Camera> ();
		startHeight = height;
	}

	private void Update ()
	{
		UpdatePos ();

		//if (!FairyGUI.Stage.isTouchOnUI)
		{
			//scroll
			float scroll = Input.mouseScrollDelta.y;
			height -= scroll;
			height = Mathf.Clamp (height, heightRange.x, heightRange.y);
			camera.orthographicSize = height = Mathf.Clamp (height, heightRange.x, heightRange.y);
		}

		//rotate
//		if (!Main.Game.Battle.InBattle)
//		{
//			if (Input.GetKeyDown (KeyCode.Q))
//			{
//				horizontalAngle += rotateAngle;
//			}
//			if (Input.GetKeyDown (KeyCode.E))
//			{
//				horizontalAngle -= rotateAngle;
//			}
//		}

		//move
		if (IsAtTopBoundary () || Input.GetKey (KeyCode.W))
		{
			MoveForward ();
		}

		if (IsAtBottomBoundary () || Input.GetKey (KeyCode.S))
		{
			MoveBack ();
		}

		if (IsAtLeftBoundary () || Input.GetKey (KeyCode.A))
		{
			MoveLeft ();
		}

		if (IsAtRightBoundary () || Input.GetKey (KeyCode.D))
		{
			MoveRight ();
		}

		//angle mode
//		if (Input.GetMouseButtonDown (2))
//		{
//			current_DepressionAngle_Index = depressionAngle_List.Count == 0 ? 0 : (++current_DepressionAngle_Index) % depressionAngle_List.Count;
//		}

		bool IsAtTopBoundary () => Input.mousePosition.y >= Screen.height - 1 && Application.isFocused && enableBoundaryMovement ;
		bool IsAtBottomBoundary () => Input.mousePosition.y <= 0 && enableBoundaryMovement && Application.isFocused;
		bool IsAtRightBoundary () => Input.mousePosition.x >= Screen.width - 1 && Application.isFocused && enableBoundaryMovement;
		bool IsAtLeftBoundary () => Input.mousePosition.x <= 0 && Application.isFocused && enableBoundaryMovement;
	}

	public bool enableBoundaryMovement = true;

	void UpdatePos ()
	{
		float d = height / Mathf.Tan (DepressionAngle * Mathf.Deg2Rad);
		Vector3 d_pos = Quaternion.Euler (0, horizontalAngle, 0) * Vector3.back * d + Vector3.up * height;
		transform.position = focusPoint + d_pos;
//Debug.Log ($"focusPoint: {focusPoint} d_pos: {d_pos}");
		//lookat
		transform.LookAt (focusPoint);
	}

	public void SwitchCameraDepressAngle ()
	{
		current_DepressionAngle_Index = depressionAngle_List.Count == 0 ? 0 : (++current_DepressionAngle_Index) % depressionAngle_List.Count;
	}
	
	public void RotateCounterclockwise ()
	{
		//if (!Main.Game.Battle.InBattle)
		{
			horizontalAngle += rotateAngle;
		}
	}
	
	public void RotateClockwise ()
	{
		//if (!Main.Game.Battle.InBattle)
		{
			horizontalAngle -= rotateAngle;
		}
	}
	
	public void MoveForward ()
	{
		Move (moveSpeed * Time.deltaTime * Vector2.up);
	}
	
	public void MoveBack ()
	{
		Move (moveSpeed * Time.deltaTime * Vector2.down);
	}
	
	public void MoveLeft ()
	{
		Move (moveSpeed * Time.deltaTime * Vector2.left);
	}
	
	public void MoveRight ()
	{
		Move (moveSpeed * Time.deltaTime * Vector2.right);
	}
	
	public void Move (Vector2 movement)
	{
		var forward = transform.forward.SetY (0).normalized;
		var right = transform.right.SetY (0).normalized;
		focusPoint += right * movement.x + forward * movement.y;

		var x = Mathf.Clamp (focusPoint.x, range_min.x, range_max.x);
		var z = Mathf.Clamp (focusPoint.z, range_min.y, range_max.y);
		focusPoint = new Vector3 (x, 0, z);
	}

	public void Reset ()
	{
		focusPoint = Vector3.zero;
		horizontalAngle = 0;
		current_DepressionAngle_Index = 0;
		height = startHeight;
	}

	public Ray ScreenMouseRay => camera.ScreenPointToRay (Input.mousePosition);

	public Vector2Int? GetMouseCoord ()
	{
		Plane plane = new Plane (Vector3.down, Terrain.transform.position);
		var ray = ScreenMouseRay;
		if (plane.Raycast (ray, out float enter))
		{
			Vector3 pos = ray.GetPoint (enter);
			return Terrain.PositionToCoord (pos);
		}
		return null;
	}

	public Vector3 WorldToScreen (Vector3 worldPos)
	{
		return camera.WorldToScreenPoint (worldPos);
	}

}
